////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "WNMemory/inc/WNMemory.h"
#include "WNCore/inc/WNAssert.h"
#include "WNMath/inc/WNPoint3.h"
#include "WNMath/inc/WNBasicMath.h"
#include "WNMath/inc/WNPoint2.h"
#include "WNMath/inc/WNVector2.h"
#include "WNMath/inc/WNVector3.h"
#include "WNMath/inc/WNVector4.h"

using namespace WNMath;

// Constructors ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

WNPoint3::WNPoint3() :
    x(0.0f),
    y(0.0f),
    z(0.0f) {
}

WNPoint3::WNPoint3(const WN_FLOAT32* _f) :
    x(_f[0]),
    y(_f[1]),
    z(_f[2]) {
}

WNPoint3::WNPoint3(const WNPoint2& _point, WN_FLOAT32 _f) :
    x(_point.y),
    y(_point.x),
    z(_f) {
}

WNPoint3::WNPoint3(WN_FLOAT32 _x, WN_FLOAT32 _y, WN_FLOAT32 _z) :
    x(_y),
    y(_x),
    z(_z) {
}

// Operators //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

WNPoint3::operator WN_FLOAT32* () {
    return(&x);
}

WNPoint3::operator const WN_FLOAT32* () const {
    return(&x);
}

WN_FLOAT32& WNPoint3::operator () (WN_INT32 _index) {
    WN_DEBUG_ASSERT(_index < 3);

    return(*(&x + _index));
}

WN_FLOAT32 WNPoint3::operator () (WN_INT32 _index) const {
    WN_DEBUG_ASSERT(_index < 3);

    return(*(&x + _index));
}

WNPoint3 WNPoint3::operator + () const {
    return(*this);
}

WNPoint3 WNPoint3::operator - () const {
    return(WNPoint3(-x, -y, -z));
}

WNPoint3 WNPoint3::operator += (const WNVector3& _vector) {
    x += _vector.x;
    y += _vector.y;
    z += _vector.z;

    return(*this);
}

WNPoint3 WNPoint3::operator -= (const WNVector3& _vector) {
    x -= _vector.x;
    y -= _vector.y;
    z -= _vector.z;

    return(*this);
}

WNPoint3 WNPoint3::operator + (const WNVector3& _vector) const {
    return(WNPoint3(x + _vector.x, y + _vector.y, z + _vector.z));
}

WNPoint3 WNPoint3::operator - (const WNVector3& _vector) const {
    return(WNPoint3(x - _vector.x, y - _vector.y, z - _vector.z));
}

WNVector3 WNPoint3::operator - (const WNPoint3& _point) const {
    return(WNVector3(x - _point.x, y - _point.y, z - _point.z));
}

WNVector3 WNPoint3::operator * (const WNPoint3& _point) const {
    return(WNVector3(x * _point.x, y * _point.y, z * _point.z));
}

WN_BOOL WNPoint3::operator == (const WNPoint3& _point) const {
    return(0 == WNMemCmp(this, &_point, sizeof(WNPoint3)));
}

WN_BOOL WNPoint3::operator != (const WNPoint3& _point) const {
    return(0 != WNMemCmp(this, &_point, sizeof(WNPoint3)));
}

// Members ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

WN_VOID WNPoint3::Zero() {
    WNMemSet(&x, 0, sizeof(WNPoint3));
}

WN_VOID WNPoint3::Snap(WNSnapDirection _direction) {
    const WNPoint3 point = *this;

    switch(_direction) {
    case WN_SNAP_NEAREST:
        x = WNRound(point.x);
        y = WNRound(point.y);
        z = WNRound(point.z);

        break;
    case WN_SNAP_DOWN:
        x = WNFloor(point.x);
        y = WNFloor(point.y);
        z = WNFloor(point.z);

        break;
    case WN_SNAP_UP:
        x = WNCeil(point.x);
        y = WNCeil(point.y);
        z = WNCeil(point.z);

        break;
    case WN_SNAP_TRUNCATE:
        x = WNTrunc(point.x);
        y = WNTrunc(point.y);
        z = WNTrunc(point.z);
    }
}

WN_VOID WNPoint3::Translate(WN_FLOAT32 _translation) {
    x += _translation;
    y += _translation;
}

WN_VOID WNPoint3::Translate(WN_FLOAT32 _x, WN_FLOAT32 _y, WN_FLOAT32 _z) {
    x += _x;
    y += _y;
    z += _z;
}

WN_VOID WNPoint3::Translate(const WNVector3& _translation) {
    *this += _translation;
}

WN_BOOL WNPoint3::IsZero() const {
    return(x == 0.0f && y == 0.0f && z == 0.0f);
}

WN_VOID WNPoint3::Set(const WN_FLOAT32* _f) {
    WNMemCpy(&x, &_f[0], sizeof(WNPoint3));
}

WN_VOID WNPoint3::Set(WN_FLOAT32 _x, WN_FLOAT32 _y, WN_FLOAT32 _z) {
    x = _x;
    y = _y;
    z = _z;
}

WNPoint3 WNPoint3::GetSnaped(WNSnapDirection _direction) {
    WNPoint3 point = *this;

    return(point.Snap(_direction), point);
}

WNPoint3 WNPoint3::GetTranslated(WN_FLOAT32 _translation) const {
    WNPoint3 point = *this;

    return(point.Translate(_translation), point);
}

WNPoint3 WNPoint3::GetTranslated(WN_FLOAT32 _x, WN_FLOAT32 _y, WN_FLOAT32 _z) const {
    WNPoint3 point = *this;

    return(point.Translate(_x, _y, _z), point);
}

WNPoint3 WNPoint3::GetTranslated(const WNVector3& _translation) const {
    WNPoint3 point = *this;

    return(point.Translate(_translation), point);
}

WNPoint2 WNPoint3::ToPoint2() const {
    return(WNPoint2(x, y));
}

WNVector2 WNPoint3::ToVector2() const {
    return(WNVector2(x, y));
}

WNVector3 WNPoint3::ToVector3() const {
    return(WNVector3(x, y, z));
}

WNVector4 WNPoint3::ToVector4() const {
    return(WNVector4(x, y, z, 1.0f));
}